﻿using System;
using System.IO;
using System.Collections.ObjectModel;
using System.Data;
using System.Data.SqlClient;
using System.Web;
using System.Web.Security;
using System.Configuration;
using System.Web.Hosting;
using VioletDevelopmentLaboratory.Net.Data;
using VioletDevelopmentLaboratory.Net.Web.Configuration;

namespace VioletDevelopmentLaboratory.Net.Web.Component
{
	public enum BoardCommand { ListView, ThreadWrite, ThreadReplyWrite, Read, CommentWrite, AnnoWrite, FileUpload, FileDownload }
	public enum BoardPart { List, View, Form_Add, Form_Modify }

	public enum BoardCreateResult { Success, Exists }
	public enum BoardUpdateResult { Success, InvalidBoard }
	public enum BoardDeleteResult { Success, InvalidBoard }

	public enum BoardCategoryCreateResult { Success, Exists }
	public enum BoardCategoryUpdateResult { Success, InvalidCategory }
	public enum BoardCategoryDeleteResult { Success, InvalidCategory }
	public enum BoardCategoryDeleteIconResult { Success, EmptyIcon, InvalidCategory }
	public enum BoardCategoryMoveResult { Success, InvalidSourceCategory, InvalidDestBoard, ExistsDestCategory }

	public enum BoardThreadCreateResult { Success, Failure }
	public enum BoardThreadUpdateResult { Success, InvalidThread }
	public enum BoardThreadDeleteResult { Success, InvalidThread }
	public enum BoardThreadSetRatingResult { Success, Already, InvalidThread }
	public enum BoardThreadSetBlockResult { Success, InvalidThread }
	public enum BoardThreadSetClearResult { Success, InvalidThread }
	public enum BoardThreadMoveResult { Success, InvalidSourceBoardThread, InvalidDestBoard, InvalidDestCategory }

	public enum BoardCommentCreateResult { Success, InvalidThread }
	public enum BoardCommentUpdateResult { Success, InvalidComment }
	public enum BoardCommentDeleteResult { Success, InvalidComment }
	public enum BoardCommentSetClearResult { Success, InvalidComment }
	public enum BoardCommentSetBlockResult { Success, InvalidComment }

	public class Board : ComponentBase
	{
		#region 속성
		#region Basic
		private String pageUrl;
		public String PageUrl { get { return this.pageUrl; } }

		private Int32 pageSize;
		public Int32 PageSize { get { return this.pageSize; } }

		private Int32 pageButtonCount;
		public Int32 PageButtonCount { get { return this.pageButtonCount; } }

		private Int32 threadLimit;
		public Int32 ThreadLimit { get { return this.threadLimit; } }
		#endregion


		#region Options
		private Boolean useComment;
		public Boolean UseComment { get { return this.useComment; } }

		private Boolean useFileUpload;
		public Boolean UseFileUpload { get { return this.useFileUpload; } }

		private Boolean useListView;
		public Boolean UseListView { get { return this.useListView; } }

		private Boolean useReply;
		public Boolean UseReply { get { return this.useReply; } }

		private Boolean useStressHot;
		public Boolean UseStressHot { get { return this.useStressHot; } }

		private Boolean useStressNew;
		public Boolean UseStressNew { get { return this.useStressNew; } }

		private Boolean useStressToday;
		public Boolean UseStressToday { get { return this.useStressToday; } }

		private Boolean useVote;
		public Boolean UseVote { get { return this.useVote; } }

		private Boolean requiredCategory;
		public Boolean RequiredCategory { get { return this.requiredCategory; } }

		private Int32 hotCount;
		public Int32 HotCount { get { return this.hotCount; } }
		#endregion


		#region Attachment
		private Int32 fileUploadLimit;
		public Int32 FileUploadLimit { get { return this.fileUploadLimit; } }

		private Int32 fileUploadSizeLimit;
		public Int32 FileUploadSizeLimit { get { return this.fileUploadSizeLimit; } }

		private String fileUploadExtension;
		public String FileUploadExtension { get { return this.fileUploadExtension; } }
		#endregion


		#region Thumbnail
		private Int32 thumbnailWidth;
		public Int32 ThumbnailWidth { get { return this.thumbnailWidth; } }

		private Int32 thumbnailHeight;
		public Int32 ThumbnailHeight { get { return this.thumbnailHeight; } }

		private Boolean thumbnailSourceAspectRatio;
		public Boolean ThumbnailSourceAspectRatio { get { return this.thumbnailSourceAspectRatio; } }
		#endregion


		#region Roles
		private String annoWriteRoles;
		public String AnnoWriteRoles { get { return this.annoWriteRoles; } }

		private String commentWriteRoles;
		public String CommentWriteRoles { get { return this.commentWriteRoles; } }

		private String fileDownloadRoles;
		public String FileDownloadRoles { get { return this.fileDownloadRoles; } }

		private String fileUploadRoles;
		public String FileUploadRoles { get { return this.fileUploadRoles; } }

		private String listViewRoles;
		public String ListViewRoles { get { return this.listViewRoles; } }

		private String readRoles;
		public String ReadRoles { get { return this.readRoles; } }

		private String threadReplyWriteRoles;
		public String ThreadReplyWriteRoles { get { return this.threadReplyWriteRoles; } }

		private String threadWriteRoles;
		public String ThreadWriteRoles { get { return this.threadWriteRoles; } }
		#endregion

		private SysopCollection sysopCollection;
		public SysopCollection SysopCollection { get { return this.sysopCollection; } internal set { this.sysopCollection = value; } }
		#endregion


		#region 게시판
		public BoardUpdateResult ModifyPageUrl(String pageUrl)
		{
			if (pageUrl == null)
				throw new ArgumentNullException("pageUrl");

			if (String.IsNullOrEmpty(pageUrl))
				throw new ArgumentOutOfRangeException("pageUrl");

			HttpApplicationState applicationState = HttpContext.Current.Application;
			applicationState.Lock();
			applicationState.Remove("VWebComponent_Board_" + this.ComponentID);
			applicationState.UnLock();

			Object returnValue = null;

			SqlClientAgent.ExecuteNonQuery(
				ConfigurationManager.ConnectionStrings[VWebConfigurationReader.ConnectionStringName].ConnectionString,
				CommandType.StoredProcedure, "VWeb_Board_UpdatePageUrl",
				out returnValue,
				new SqlParameter("@ApplicationName", VWebConfigurationReader.ApplicationName),
				new SqlParameter("@ComponentID", this.ComponentID),
				new SqlParameter("@PageUrl", pageUrl)
			);

			switch ((Int32)returnValue)
			{
				case 0:
					return BoardUpdateResult.Success;
				default:
					return BoardUpdateResult.InvalidBoard;
			}
		}


		public static BoardCreateResult Add(String componentID, String name, Boolean useComment, Boolean useReply,
			Boolean useFileUpload, Boolean useStressNew, Boolean useStressToday, Boolean useStressHot,
			Boolean useListView, Boolean useVote, Int32 hotCount, Int32 threadLimit, Int32 pageSize,
			Int32 pageButtonCount, Int32 fileUploadLimit, Int32 fileUploadSizeLimit, String fileUploadExtension,
			Int32 thumbnailWidth, Int32 thumbnailHeight, Boolean thumbnailSourceAspectRatio, String listViewRoles,
			String annoWriteRoles, String threadWriteRoles, String threadReplyWriteRoles, String readRoles,
			String commentWriteRoles, String fileUploadRoles, String fileDownloadRoles, Boolean requiredCategory)
		{
			if (componentID == null)
				throw new ArgumentNullException("componentID");

			if (name == null)
				throw new ArgumentNullException("name");

			if (String.IsNullOrEmpty(componentID))
				throw new ArgumentOutOfRangeException("componentID");

			if (String.IsNullOrEmpty(name))
				throw new ArgumentOutOfRangeException("name");

            Object returnValue = null;

			SqlClientAgent.ExecuteNonQuery(
				ConfigurationManager.ConnectionStrings[VWebConfigurationReader.ConnectionStringName].ConnectionString,
                CommandType.StoredProcedure, "VWeb_Board_Create",
                out returnValue,
				new SqlParameter("@ApplicationName", VWebConfigurationReader.ApplicationName),
				new SqlParameter("@ComponentID", componentID),
				new SqlParameter("@Name", name),
				new SqlParameter("@UseComment", useComment),
				new SqlParameter("@UseReply", useReply),
				new SqlParameter("@UseFileUpload", useFileUpload),
				new SqlParameter("@UseStressNew", useStressNew),
				new SqlParameter("@UseStressToday", useStressToday),
				new SqlParameter("@UseStressHot", useStressHot),
				new SqlParameter("@UseListView", useListView),
				new SqlParameter("@UseVote", useVote),
				new SqlParameter("@HotCount", hotCount),
				new SqlParameter("@ThreadLimit", threadLimit),
				new SqlParameter("@PageSize", pageSize),
				new SqlParameter("@PageButtonCount", pageButtonCount),
				new SqlParameter("@FileUploadLimit", fileUploadLimit),
				new SqlParameter("@FileUploadSizeLimit", fileUploadSizeLimit),
				new SqlParameter("@FileUploadExtension", fileUploadExtension),
				new SqlParameter("@ThumbnailWidth", thumbnailWidth),
				new SqlParameter("@ThumbnailHeight", thumbnailHeight),
				new SqlParameter("@ThumbnailSourceAspectRatio", thumbnailSourceAspectRatio),
				new SqlParameter("@ListViewRoles", listViewRoles),
				new SqlParameter("@AnnoWriteRoles", annoWriteRoles),
				new SqlParameter("@ThreadWriteRoles", threadWriteRoles),
				new SqlParameter("@ThreadReplyWriteRoles", threadReplyWriteRoles),
				new SqlParameter("@ReadRoles", readRoles),
				new SqlParameter("@CommentWriteRoles", commentWriteRoles),
				new SqlParameter("@FileUploadRoles", fileUploadRoles),
				new SqlParameter("@FileDownloadRoles", fileDownloadRoles),
				new SqlParameter("@RequiredCategory", requiredCategory)
			);

            switch ((Int32)returnValue)
            {
                case 0 :
                    return BoardCreateResult.Success;
				default:
					return BoardCreateResult.Exists;
            }
		}

		public static BoardUpdateResult Modify(String componentID, String name, Boolean useComment, Boolean useReply,
			Boolean useFileUpload, Boolean useStressNew, Boolean useStressToday, Boolean useStressHot,
			Boolean useListView, Boolean useVote, Int32 hotCount, Int32 threadLimit, Int32 pageSize,
			Int32 pageButtonCount, Int32 fileUploadLimit, Int32 fileUploadSizeLimit, String fileUploadExtension,
			Int32 thumbnailWidth, Int32 thumbnailHeight, Boolean thumbnailSourceAspectRatio, String listViewRoles,
			String annoWriteRoles, String threadWriteRoles, String threadReplyWriteRoles, String readRoles,
			String commentWriteRoles, String fileUploadRoles, String fileDownloadRoles, Boolean requiredCategory)
		{
			if (componentID == null)
				throw new ArgumentNullException("componentID");

			if (name == null)
				throw new ArgumentNullException("name");

			if (String.IsNullOrEmpty(componentID))
				throw new ArgumentOutOfRangeException("componentID");

			if (String.IsNullOrEmpty(name))
				throw new ArgumentOutOfRangeException("name");

			HttpApplicationState applicationState = HttpContext.Current.Application;
			applicationState.Lock();
			applicationState.Remove("VWebComponent_Board_" + componentID);
			applicationState.UnLock();

            Object returnValue = null;

			SqlClientAgent.ExecuteNonQuery(
				ConfigurationManager.ConnectionStrings[VWebConfigurationReader.ConnectionStringName].ConnectionString,
                CommandType.StoredProcedure, "VWeb_Board_Update",
                out returnValue,
				new SqlParameter("@ApplicationName", VWebConfigurationReader.ApplicationName),
				new SqlParameter("@ComponentID", componentID),
				new SqlParameter("@Name", name),
				new SqlParameter("@UseComment", useComment),
				new SqlParameter("@UseReply", useReply),
				new SqlParameter("@UseFileUpload", useFileUpload),
				new SqlParameter("@UseStressNew", useStressNew),
				new SqlParameter("@UseStressToday", useStressToday),
				new SqlParameter("@UseStressHot", useStressHot),
				new SqlParameter("@UseListView", useListView),
				new SqlParameter("@UseVote", useVote),
				new SqlParameter("@HotCount", hotCount),
				new SqlParameter("@ThreadLimit", threadLimit),
				new SqlParameter("@PageSize", pageSize),
				new SqlParameter("@PageButtonCount", pageButtonCount),
				new SqlParameter("@FileUploadLimit", fileUploadLimit),
				new SqlParameter("@FileUploadSizeLimit", fileUploadSizeLimit),
				new SqlParameter("@FileUploadExtension", fileUploadExtension),
				new SqlParameter("@ThumbnailWidth", thumbnailWidth),
				new SqlParameter("@ThumbnailHeight", thumbnailHeight),
				new SqlParameter("@ThumbnailSourceAspectRatio", thumbnailSourceAspectRatio),
				new SqlParameter("@ListViewRoles", listViewRoles),
				new SqlParameter("@AnnoWriteRoles", annoWriteRoles),
				new SqlParameter("@ThreadWriteRoles", threadWriteRoles),
				new SqlParameter("@ThreadReplyWriteRoles", threadReplyWriteRoles),
				new SqlParameter("@ReadRoles", readRoles),
				new SqlParameter("@CommentWriteRoles", commentWriteRoles),
				new SqlParameter("@FileUploadRoles", fileUploadRoles),
				new SqlParameter("@FileDownloadRoles", fileDownloadRoles),
				new SqlParameter("@RequiredCategory", requiredCategory)
			);

            switch ((Int32)returnValue)
            {
                case 0 :
                    return BoardUpdateResult.Success;
				default :
					return BoardUpdateResult.InvalidBoard;
            }
		}

		public static BoardDeleteResult Remove(String componentID)
		{
			if (componentID == null)
				throw new ArgumentNullException("componentID");

			if (String.IsNullOrEmpty(componentID))
				throw new ArgumentOutOfRangeException("componentID");

			Board.GetBoard(componentID).GetAttachmentFileManager().RemoveAll();

			HttpApplicationState applicationState = HttpContext.Current.Application;
			applicationState.Lock();
			applicationState.Remove("VWebComponent_Board_" + componentID);
			applicationState.UnLock();

            Object returnValue = null;

			SqlClientAgent.ExecuteNonQuery(
				ConfigurationManager.ConnectionStrings[VWebConfigurationReader.ConnectionStringName].ConnectionString,
                CommandType.StoredProcedure, "VWeb_Board_Delete",
                out returnValue,
				new SqlParameter("@ApplicationName", VWebConfigurationReader.ApplicationName),
				new SqlParameter("@componentID", componentID)
			);

            switch ((Int32)returnValue)
            {
                case 0 :
                    return BoardDeleteResult.Success;
				default:
					return BoardDeleteResult.InvalidBoard;
            }
		}

		private static Board fillBoard(SqlDataReader reader)
		{
			Board board = new Board();
			board.ApplicationName = reader["ApplicationName"].ToString();
			board.ComponentID = reader["ComponentID"].ToString();
			board.Name = reader["Name"].ToString();
			board.pageUrl = reader["PageUrl"].ToString();
			board.useComment = (Boolean)reader["UseComment"];
			board.useReply = (Boolean)reader["UseReply"];
			board.useFileUpload = (Boolean)reader["UseFileUpload"];
			board.useStressNew = (Boolean)reader["UseStressNew"];
			board.useStressToday = (Boolean)reader["UseStressToday"];
			board.useStressHot = (Boolean)reader["UseStressHot"];
			board.useListView = (Boolean)reader["UseListView"];
			board.useVote = (Boolean)reader["UseVote"];
			board.hotCount = (Int32)reader["HotCount"];
			board.threadLimit = (Int32)reader["ThreadLimit"];
			board.pageSize = (Int32)reader["PageSize"];
			board.pageButtonCount = (Int32)reader["PageButtonCount"];
			board.fileUploadLimit = (Int32)reader["FileUploadLimit"];
			board.fileUploadSizeLimit = (Int32)reader["FileUploadSizeLimit"];
			board.fileUploadExtension = reader["FileUploadExtension"].ToString();
			board.thumbnailWidth = (Int32)reader["ThumbnailWidth"];
			board.thumbnailHeight = (Int32)reader["ThumbnailHeight"];
			board.thumbnailSourceAspectRatio = (Boolean)reader["ThumbnailSourceAspectRatio"];
			board.listViewRoles = reader["ListViewRoles"].ToString();
			board.annoWriteRoles = reader["AnnoWriteRoles"].ToString();
			board.threadWriteRoles = reader["ThreadWriteRoles"].ToString();
			board.threadReplyWriteRoles = reader["ThreadReplyWriteRoles"].ToString();
			board.commentWriteRoles = reader["CommentWriteRoles"].ToString();
			board.readRoles = reader["ReadRoles"].ToString();
			board.fileUploadRoles = reader["FileUploadRoles"].ToString();
			board.fileDownloadRoles = reader["FileDownloadRoles"].ToString();
			board.CreationDate = (DateTime)reader["CreationDate"];
			board.requiredCategory = (Boolean)reader["RequiredCategory"];

			return board;
		}

		public static Board GetBoard(String componentID)
		{
			if (componentID == null)
				throw new ArgumentNullException("componentID");

			if (String.IsNullOrEmpty(componentID))
				throw new ArgumentOutOfRangeException("componentID");

			HttpApplicationState applicationState = HttpContext.Current.Application;
			String componentKey = "VWebComponent_Board_" + componentID;
			if (applicationState[componentKey] != null)
			{
				return (Board)applicationState[componentKey];
			}

			SqlDataReader reader = SqlClientAgent.ExecuteReader(
				ConfigurationManager.ConnectionStrings[VWebConfigurationReader.ConnectionStringName].ConnectionString,
				CommandType.Text,
				"EXEC [VWeb_Board_GetBoard] @ApplicationName, @ComponentID;" +
				"EXEC [VWeb_Sysop_GetSysops] @ApplicationName, @ComponentID;",
				new SqlParameter("@ApplicationName", VWebConfigurationReader.ApplicationName),
				new SqlParameter("@ComponentID", componentID)
			);

			Board board = null;

			try
			{
				if (reader.HasRows)
				{
					reader.Read();
					board = fillBoard(reader);

					if (reader.NextResult())
					{
						if (reader.HasRows)
						{
							board.SysopCollection = new SysopCollection();

							while (reader.Read())
							{
								board.SysopCollection.Add(Sysop.Fill(reader));
							}
						}
					}
				}
			}
			finally
			{
				reader.Close();
			}

			applicationState.Lock();
			applicationState.Add(componentKey, board);
			applicationState.UnLock();

			return board;
		}

		public static BoardCollection GetBoardCollection()
		{
			SqlDataReader reader = SqlClientAgent.ExecuteReader(
				ConfigurationManager.ConnectionStrings[VWebConfigurationReader.ConnectionStringName].ConnectionString,
				CommandType.StoredProcedure, "VWeb_Board_GetBoards",
				new SqlParameter("@ApplicationName", VWebConfigurationReader.ApplicationName)
			);

			BoardCollection boardCollection = null;

			try
			{
				if (reader.HasRows)
				{
					boardCollection = new BoardCollection();

					while (reader.Read())
					{
						boardCollection.Add(fillBoard(reader));
					}
				}
			}
			finally
			{
				reader.Close();
			}

			return boardCollection;
		}
		#endregion


		#region 분류(BoardCategory)
		public BoardCategory GetCategoryByID(String categoryID)
		{
			return BoardCategory.GetCategoryByID(this.ComponentID, categoryID);
		}

		public BoardCategoryCollection GetCategoryCollection()
		{
			return BoardCategory.GetCategoryCollection(this.ComponentID);
		}
		#endregion


		#region 글(BoardThread)
		public BoardThreadCreateResult AddThread(String categoryID, Int64 threadGroup, Int32 threadDepth,
			Int32 threadIndex, String userName, String providerUserKey, Boolean isAnno, Boolean isSecret, Boolean isClosed,
			String ip, String[] links, String[] fileIDs, Boolean autoMediaFileView, String title, Boolean isHTML, String[] tags, String content,
			out String insertedThreadID, out Int64 insertedThreadNumber, out Int64 insertedThreadGroup)
		{
			if (userName == null)
				throw new ArgumentNullException("userName");

			if (providerUserKey == null)
				throw new ArgumentNullException("providerUserKey");

			if (ip == null)
				throw new ArgumentNullException("ip");

			if (title == null)
				throw new ArgumentNullException("title");

			if (content == null)
				throw new ArgumentNullException("content");

			if (String.IsNullOrEmpty(userName))
				throw new ArgumentOutOfRangeException("userName");

			if (String.IsNullOrEmpty(providerUserKey))
				throw new ArgumentOutOfRangeException("providerUserKey");

			if (String.IsNullOrEmpty(ip))
				throw new ArgumentOutOfRangeException("ip");

			if (String.IsNullOrEmpty(title))
				throw new ArgumentOutOfRangeException("title");

			if (String.IsNullOrEmpty(content))
				throw new ArgumentOutOfRangeException("content");

			String linksString = "";

			if (links != null)
			{
				foreach (String link in links)
				{
					if (!String.IsNullOrEmpty(link.Trim()))
						if (System.Text.RegularExpressions.Regex.IsMatch(link, VUtility.GetVWebGlobalResource("General_Expression_Url")))
							linksString += link.Trim() + "\r\n";
				}
			}

			String tagsString = "";

			if (tags != null)
			{
				foreach (String tag in tags)
				{
					if (!String.IsNullOrEmpty(tag.Trim()))
						tagsString += tag.Trim() + ",";
				}

				if (tagsString.Length > 0)
					tagsString = tagsString.Substring(0, tagsString.Length - 1);
			}

			Object returnValue = null;

			SqlParameter insertedThreadIDParameter = new SqlParameter();
			insertedThreadIDParameter.ParameterName = "@InsertedThreadID";
			insertedThreadIDParameter.SqlDbType = SqlDbType.UniqueIdentifier;
			insertedThreadIDParameter.Direction = ParameterDirection.Output;

			SqlParameter insertedThreadNumberParameter = new SqlParameter();
			insertedThreadNumberParameter.ParameterName = "@insertedThreadNumber";
			insertedThreadNumberParameter.SqlDbType = SqlDbType.BigInt;
			insertedThreadNumberParameter.Direction = ParameterDirection.Output;

			SqlParameter insertedThreadGroupParameter = new SqlParameter();
			insertedThreadGroupParameter.ParameterName = "@insertedThreadGroup";
			insertedThreadGroupParameter.SqlDbType = SqlDbType.BigInt;
			insertedThreadGroupParameter.Direction = ParameterDirection.Output;

			SqlClientAgent.ExecuteNonQuery(
				ConfigurationManager.ConnectionStrings[VWebConfigurationReader.ConnectionStringName].ConnectionString,
				CommandType.StoredProcedure,
				"VWeb_BoardThread_Create",
				out returnValue,
				new SqlParameter("@ApplicationName", VWebConfigurationReader.ApplicationName),
				new SqlParameter("@ComponentID", this.ComponentID),
				String.IsNullOrEmpty(categoryID) ? new SqlParameter("@CategoryID", DBNull.Value) : new SqlParameter("@CategoryID", categoryID),
				threadGroup > 0 ? new SqlParameter("@ThreadGroup", threadGroup) : new SqlParameter("@ThreadGroup", DBNull.Value),
				new SqlParameter("@ThreadDepth", threadDepth),
				new SqlParameter("@ThreadIndex", threadIndex),
				new SqlParameter("@UserName", userName),
				new SqlParameter("@ProviderUserKey", providerUserKey),
				new SqlParameter("@IsAnno", isAnno),
				new SqlParameter("@IsSecret", isSecret),
				new SqlParameter("@IsClosed", isClosed),
				new SqlParameter("@Ip", ip),
				String.IsNullOrEmpty(linksString) ? new SqlParameter("@Links", DBNull.Value) : new SqlParameter("@Links", linksString),
				new SqlParameter("@AutoMediaFileView", autoMediaFileView),
				new SqlParameter("@Title", title),
				new SqlParameter("@IsHTML", isHTML),
				String.IsNullOrEmpty(tagsString) ? new SqlParameter("@Tags", DBNull.Value) : new SqlParameter("@Tags", tagsString),
				new SqlParameter("@Content", content),
				insertedThreadIDParameter,
				insertedThreadNumberParameter,
				insertedThreadGroupParameter
			);

			insertedThreadID = insertedThreadIDParameter.Value.ToString();
			insertedThreadNumber = (Int64)insertedThreadNumberParameter.Value;
			insertedThreadGroup = (Int64)insertedThreadGroupParameter.Value;

			//태그 추가
			foreach (String tag in tags)
			{
				if (!String.IsNullOrEmpty(tag.Trim()))
					Tag.Add(this.ComponentID, insertedThreadID, tag.Trim());
			}

			//첨부파일 연결
			Board.GetBoard(this.ComponentID).GetAttachmentFileManager().LinkRelationID(fileIDs, insertedThreadID);

			VUtility.SendSmtpMessage("BoardThread.Add", ip, userName, title, content);

			switch ((Int32)returnValue)
			{
				case 0:
					return BoardThreadCreateResult.Success;
				default:
					return BoardThreadCreateResult.Failure;
			}
		}

		public BoardThread GetThread(String threadID)
		{
			if (threadID == null)
				throw new ArgumentNullException("threadID");

			if (String.IsNullOrEmpty(threadID))
				throw new ArgumentOutOfRangeException("threadID");

			if (!VUtility.IsGuid(threadID))
				throw new FormatException("threadID must Guid.");

			SqlDataReader reader = SqlClientAgent.ExecuteReader(
				ConfigurationManager.ConnectionStrings[VWebConfigurationReader.ConnectionStringName].ConnectionString,
				CommandType.StoredProcedure,
				"VWeb_BoardThread_GetBoardThread",
				new SqlParameter("@ApplicationName", VWebConfigurationReader.ApplicationName),
				new SqlParameter("@ComponentID", this.ComponentID),
				new SqlParameter("@ThreadId", threadID)
			);

			BoardThread thread = null;

			try
			{
				if (reader != null && reader.HasRows)
				{
					reader.Read();
					thread = BoardThread.Fill(reader);
				}
			}
			finally
			{
				if (reader != null)
					reader.Close();
			}

			return thread;
		}

		public BoardThreadCollection GetThreadCollection(String categoryID, String searchText, Int64 pageIndex)
		{
			SqlDataReader reader = SqlClientAgent.ExecuteReader(
				ConfigurationManager.ConnectionStrings[VWebConfigurationReader.ConnectionStringName].ConnectionString,
				CommandType.StoredProcedure,
				"VWeb_BoardThread_GetBoardThreads",
				new SqlParameter("@ApplicationName", VWebConfigurationReader.ApplicationName),
				new SqlParameter("@ComponentID", this.ComponentID),
				String.IsNullOrEmpty(categoryID) ? new SqlParameter("@CategoryID", DBNull.Value) : new SqlParameter("@CategoryID", categoryID),
				new SqlParameter("@SearchText", searchText),
				new SqlParameter("@MaximumRows", this.PageSize),
				new SqlParameter("@StartRowIndex", this.PageSize * pageIndex)
			);

			BoardThreadCollection threadCollection = null;

			try
			{
				if (reader.HasRows)
				{
					threadCollection = new BoardThreadCollection();

					while (reader.Read())
					{
						threadCollection.Add(BoardThread.Fill(reader));
					}
				}
			}
			finally
			{
				reader.Close();
			}

			return threadCollection;
		}

		public Int64 GetThreadCollectionCount(String categoryID, String searchText)
		{
			Object result = SqlClientAgent.ExecuteScalar(
				ConfigurationManager.ConnectionStrings[VWebConfigurationReader.ConnectionStringName].ConnectionString,
				CommandType.StoredProcedure,
				"VWeb_BoardThread_GetBoardThreadCount",
				new SqlParameter("@ApplicationName", VWebConfigurationReader.ApplicationName),
				new SqlParameter("@ComponentID", this.ComponentID),
				String.IsNullOrEmpty(categoryID) ? new SqlParameter("@CategoryID", DBNull.Value) : new SqlParameter("@CategoryID", categoryID),
				new SqlParameter("@SearchText", searchText)
			);

			return Convert.ToInt64(result);
		}

		public BoardThreadCollection GetThreadSummayCollection(Int32 pageSize)
		{
			if (pageSize < 1)
				throw new ArgumentOutOfRangeException("pageSize");

			SqlDataReader reader = SqlClientAgent.ExecuteReader(
				ConfigurationManager.ConnectionStrings[VWebConfigurationReader.ConnectionStringName].ConnectionString,
				CommandType.StoredProcedure,
				"VWeb_BoardThread_GetBoardThreadSummary",
				new SqlParameter("@ApplicationName", VWebConfigurationReader.ApplicationName),
				new SqlParameter("@ComponentID", this.ComponentID),
				new SqlParameter("@PageSize", pageSize)
			);

			BoardThreadCollection threadCollection = null;

			try
			{
				if (reader.HasRows)
				{
					threadCollection = new BoardThreadCollection();

					while (reader.Read())
					{
						threadCollection.Add(BoardThread.Fill(reader));
					}
				}
			}
			finally
			{
				reader.Close();
			}

			return threadCollection;
		}
		#endregion


		internal void GetListPartData(String categoryID, String searchText, Int64 pageIndex,
			out BoardCategoryCollection currentCategoryCollection, out BoardThreadCollection currentThreadCollection)
		{
			currentCategoryCollection = null;
			currentThreadCollection = null;

			DataSet dataSet = SqlClientAgent.ExecuteDataSet(
				ConfigurationManager.ConnectionStrings[VWebConfigurationReader.ConnectionStringName].ConnectionString,
				CommandType.Text,
				"EXEC [VWeb_BoardCategory_GetBoardCategories] @ApplicationName, @ComponentID;" +
				"EXEC [VWeb_BoardThread_GetBoardThreads] @ApplicationName, @ComponentID, @CategoryID, @SearchText, @MaximumRows, @StartRowIndex;",
				new SqlParameter("@ApplicationName", this.ApplicationName),
				new SqlParameter("@ComponentID", this.ComponentID),
				String.IsNullOrEmpty(categoryID) ? new SqlParameter("@CategoryID", DBNull.Value) : new SqlParameter("@CategoryID", categoryID),
				new SqlParameter("@SearchText", searchText),
				new SqlParameter("@MaximumRows", this.PageSize),
				new SqlParameter("@StartRowIndex", this.PageSize * pageIndex)
			);

			#region 카테고리
			if (dataSet.Tables[0].Rows.Count > 0)
			{
				currentCategoryCollection = new BoardCategoryCollection();

				foreach (DataRow row in dataSet.Tables[0].Rows)
				{
					currentCategoryCollection.Add(BoardCategory.Fill(row));
				}
			}
			#endregion

			#region 글목록
			if (dataSet.Tables[1].Rows.Count > 0)
			{
				currentThreadCollection = new BoardThreadCollection();

				foreach (DataRow row in dataSet.Tables[1].Rows)
				{
					currentThreadCollection.Add(BoardThread.Fill(row));
				}
			}
			#endregion
		}

		internal void GetViewPartData(String threadID, out BoardThread thread, out BoardThreadCollection groupThreadCollection,
			out AttachmentFileInfoCollection attachmentFileInfoCollection, out BoardCommentCollection commentCollection, out Vote vote,
			out TrackbackCollection trackbackCollection)
		{
			thread = null;
			groupThreadCollection = null;
			attachmentFileInfoCollection = null;
			commentCollection = null;
			trackbackCollection = null;

			DataSet dataSet = SqlClientAgent.ExecuteDataSet(
				ConfigurationManager.ConnectionStrings[VWebConfigurationReader.ConnectionStringName].ConnectionString,
				CommandType.Text,
				"EXEC [VWeb_BoardThread_GetBoardThread] @ApplicationName, @ComponentID, @ThreadID;" +
				"EXEC [VWeb_BoardThread_GetGroupingBoardThreads] @ApplicationName, @ComponentID, @ThreadID;" +
				"EXEC [VWeb_File_GetFiles] @ApplicationName, @ComponentID, @RelationID;" +
				"EXEC [VWeb_BoardComment_GetBoardComments] @ApplicationName, @ComponentID, @ThreadID;" +
				"EXEC [VWeb_Vote_GetVote] @ApplicationName, @ComponentID, @ThreadID;" +
				"EXEC [VWeb_Trackback_GetTrackbacksByRelationID] @ApplicationName, @ComponentID, @RelationID;",
				new SqlParameter("@ApplicationName", this.ApplicationName),
				new SqlParameter("@ComponentID", this.ComponentID),
				new SqlParameter("@ThreadID", threadID),
				new SqlParameter("@RelationID", threadID)
			);

			#region 글
			if (dataSet.Tables[0].Rows.Count > 0)
			{
				thread = BoardThread.Fill(dataSet.Tables[0].Rows[0]);
			}
			#endregion

			#region 그룹글목록
			if (dataSet.Tables[1].Rows.Count > 0)
			{
				groupThreadCollection = new BoardThreadCollection();

				foreach (DataRow row in dataSet.Tables[1].Rows)
				{
					groupThreadCollection.Add(BoardThread.Fill(row));
				}
			}
			#endregion

			#region 첨부파일
			if (dataSet.Tables[2].Rows.Count > 0)
				{
					String fileID = "";
					attachmentFileInfoCollection = new AttachmentFileInfoCollection();

					AttachmentFileInfo fileInfo = null;

					foreach (DataRow row in dataSet.Tables[2].Rows)
					{
						fileID = row["FileID"].ToString();

						fileInfo = new AttachmentFileInfo(
							VWebConfigurationReader.ApplicationName,
							this.ComponentID,
							ComponentType.Board,
							threadID,
							fileID,
							row["OriginalFileName"].ToString(),
							row["ContentType"].ToString(),
							(Int32)row["DownloadCount"],
							BoardUtil.GetAttachmentFileDownloadUrl(this.ComponentID, fileID),
							row["Description"].ToString());

						if (fileInfo.UploadFile.PhysicalFile.Exists)
							attachmentFileInfoCollection.Add(fileInfo);
					}
				}
			#endregion

			#region 댓글목록
			if (dataSet.Tables[3].Rows.Count > 0)
			{
				commentCollection = new BoardCommentCollection();
				foreach (DataRow row in dataSet.Tables[3].Rows)
				{
					commentCollection.Add(BoardComment.Fill(row));
				}
			}
			#endregion

			#region 투표
			vote = Vote.fillVote(dataSet, 4);
			#endregion

			#region 트랙백
			if (dataSet.Tables[8].Rows.Count > 0)
			{
				trackbackCollection = new TrackbackCollection();
				foreach (DataRow row in dataSet.Tables[8].Rows)
				{
					trackbackCollection.Add(Trackback.Fill(row));
				}
			}
			#endregion
		}

		internal void GetFormPartData(String threadID, out BoardCategoryCollection currentCategoryCollection,
			out BoardThread thread, out AttachmentFileInfoCollection attachmentFileInfoCollection, out Vote vote)
		{
			if (String.IsNullOrEmpty(threadID) || !VUtility.IsGuid(threadID))
			{
				threadID = Guid.Empty.ToString();
			}

			currentCategoryCollection = null;
			thread = null;
			attachmentFileInfoCollection = null;

			DataSet dataSet = SqlClientAgent.ExecuteDataSet(
				ConfigurationManager.ConnectionStrings[VWebConfigurationReader.ConnectionStringName].ConnectionString,
				CommandType.Text,
				"EXEC [VWeb_BoardCategory_GetBoardCategories] @ApplicationName, @ComponentID;" +
				"EXEC [VWeb_BoardThread_GetBoardThread] @ApplicationName, @ComponentID, @ThreadID;" +
				"EXEC [VWeb_File_GetFiles] @ApplicationName, @ComponentID, @RelationID;" +
				"EXEC [VWeb_Vote_GetVote] @ApplicationName, @ComponentID, @ThreadID;",
				new SqlParameter("@ApplicationName", this.ApplicationName),
				new SqlParameter("@ComponentID", this.ComponentID),
				new SqlParameter("@ThreadID", threadID),
				new SqlParameter("@RelationID", threadID)
			);

			#region 카테고리
			if (dataSet.Tables[0].Rows.Count > 0)
			{
				currentCategoryCollection = new BoardCategoryCollection();

				foreach (DataRow row in dataSet.Tables[0].Rows)
				{
					currentCategoryCollection.Add(BoardCategory.Fill(row));
				}
			}
			#endregion

			#region 글
			if (dataSet.Tables[1].Rows.Count > 0)
			{
				thread = BoardThread.Fill(dataSet.Tables[1].Rows[0]);
			}
			#endregion

			#region 첨부파일
			if (dataSet.Tables[2].Rows.Count > 0)
			{
				String fileID = "";
				attachmentFileInfoCollection = new AttachmentFileInfoCollection();

				AttachmentFileInfo fileInfo = null;

				foreach (DataRow row in dataSet.Tables[2].Rows)
				{
					fileID = row["FileID"].ToString();

					fileInfo = new AttachmentFileInfo(
						VWebConfigurationReader.ApplicationName,
						this.ComponentID,
						ComponentType.Board,
						threadID,
						fileID,
						row["OriginalFileName"].ToString(),
						row["ContentType"].ToString(),
						(Int32)row["DownloadCount"],
						BoardUtil.GetAttachmentFileDownloadUrl(this.ComponentID, fileID),
						row["Description"].ToString());

					if (fileInfo.UploadFile.PhysicalFile.Exists)
						attachmentFileInfoCollection.Add(fileInfo);
				}
			}
			#endregion

			#region 투표
			vote = Vote.fillVote(dataSet, 3);
			#endregion
		}

		public AttachmentFileManager GetAttachmentFileManager()
		{
			return new AttachmentFileManager(this.ComponentID, ComponentType.Board);
		}
	}

	public class BoardCollection : Collection<Board> { }
}
